// Copyright (C) 2004-2022 Artifex Software, Inc.
//
// This file is part of MuPDF.
//
// MuPDF is free software: you can redistribute it and/or modify it under the
// terms of the GNU Affero General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
//
// Alternative licensing terms are available from the licensor.
// For commercial licensing, see <https://www.artifex.com/> or contact
// Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato,
// CA 94945, U.S.A., +1(415)492-9861, for further information.

package com.artifex.mupdf.fitz;


import com.artifex.mupdf.util.Debugger;

public class Document {
    static {
        Context.init();
    }

    public static final String META_FORMAT = "format";
    public static final String META_ENCRYPTION = "encryption";
    public static final String META_INFO_AUTHOR = "info:Author";
    public static final String META_INFO_TITLE = "info:Title";
    public static final String META_INFO_SUBJECT = "info:Subject";
    public static final String META_INFO_KEYWORDS = "info:Keywords";
    public static final String META_INFO_CREATOR = "info:Creator";
    public static final String META_INFO_PRODUCER = "info:Producer";
    public static final String META_INFO_CREATIONDATE = "info:CreationDate";
    public static final String META_INFO_MODIFICATIONDATE = "info:ModDate";

    public long pointer;

    protected native void finalize();

    /**
     * fix: libc    : FORTIFY: pthread_mutex_lock called on a destroyed mutex (0xd4550a60)
     * @param filename 文件路径
     * @param options 其它选项参数
     */
    public native void saveDoc(String filename, String options);

    public void destroy() {
        finalize();
    }

    protected Document(long p) {
        pointer = p;
    }

    protected native static Document openNativeWithPath(String filename, String accelerator);

    protected native static Document openNativeWithBuffer(String magic, byte[] buffer, byte[] accelerator);

    protected native static Document openNativeWithStream(String magic, SeekableInputStream stream, SeekableInputStream accelerator);

    protected native static Document openNativeWithPathAndStream(String filename, SeekableInputStream accelerator);

    public static Document openDocument(String filename) {
        return openNativeWithPath(filename, null);
    }

    public static Document openDocument(String filename, String accelerator) {
        return openNativeWithPath(filename, accelerator);
    }

    public static Document openDocument(String filename, SeekableInputStream accelerator) {
        return openNativeWithPathAndStream(filename, accelerator);
    }

    public static Document openDocument(byte[] buffer, String magic) {
        return openNativeWithBuffer(magic, buffer, null);
    }

    public static Document openDocument(byte[] buffer, String magic, byte[] accelerator) {
        return openNativeWithBuffer(magic, buffer, accelerator);
    }

    public static Document openDocument(SeekableInputStream stream, String magic) {
        return openNativeWithStream(magic, stream, null);
    }

    public static Document openDocument(SeekableInputStream stream, String magic, SeekableInputStream accelerator) {
        return openNativeWithStream(magic, stream, accelerator);
    }

    public static native boolean recognize(String magic);

    public native boolean supportsAccelerator();

    public native void saveAccelerator(String filename);

    public native void outputAccelerator(SeekableOutputStream stream);

    public native boolean needsPassword();

    public native boolean authenticatePassword(String password);

    public native int countChapters();

    public native int countPages(int chapter);

    public native Page loadPage(int chapter, int number);

    public int countPages() {
        int np = 0;
        int nc = countChapters();
        for (int i = 0; i < nc; ++i)
            np += countPages(i);
        return np;
    }

    public Page loadPage(Location loc) {
        return loadPage(loc.chapter, loc.page);
    }

    public Page loadPage(int number) {
        int start = 0;
        int nc = countChapters();
        for (int i = 0; i < nc; ++i) {
            int np = countPages(i);
            Debugger.i("loadPage i:" + i + ",np:" + np + ",start:" + start + ",number:" + number);
            if (number < start + np) {
                return loadPage(i, number - start);
            }
            start += np;
        }
        throw new IllegalArgumentException("page number out of range");
    }

    public Location lastPage() {
        int nc = countChapters();
        int np = countPages(nc - 1);
        return new Location(nc - 1, np - 1);
    }

    public Location nextPage(Location loc) {
        int np = countPages(loc.chapter);
        if (loc.page + 1 == np) {
            int nc = countChapters();
            if (loc.chapter + 1 < nc)
                return new Location(loc.chapter + 1, 0);
        } else {
            return new Location(loc.chapter, loc.page + 1);
        }
        return loc;
    }

    public Location previousPage(Location loc) {
        if (loc.page == 0) {
            if (loc.chapter > 0) {
                int np = countPages(loc.chapter - 1);
                return new Location(loc.chapter - 1, np - 1);
            }
        } else {
            return new Location(loc.chapter, loc.page - 1);
        }
        return loc;
    }

    public Location clampLocation(Location input) {
        int c = input.chapter;
        int p = input.page;
        int nc = countChapters();
        if (c < 0) c = 0;
        if (c >= nc) c = nc - 1;
        int np = countPages(c);
        if (p < 0) p = 0;
        if (p >= np) p = np - 1;
        if (input.chapter == c && input.page == p)
            return input;
        return new Location(c, p);
    }

    public Location locationFromPageNumber(int number) {
        int i, start = 0, np = 0, nc = countChapters();
        if (number < 0)
            number = 0;
        for (i = 0; i < nc; ++i) {
            np = countPages(i);
            if (number < start + np)
                return new Location(i, number - start);
            start += np;
        }
        return new Location(i - 1, np - 1);
    }

    public int pageNumberFromLocation(Location loc) {
        int nc = countChapters();
        int start = 0;
        if (loc == null)
            return -1;
        for (int i = 0; i < nc; ++i) {
            if (i == loc.chapter)
                return start + loc.page;
            start += countPages(i);
        }
        return -1;
    }

    public native Quad[][] search(int chapter, int page, String needle);

    public native Location resolveLink(String uri);

    public Location resolveLink(Outline link) {
        return resolveLink(link.uri);
    }

    public Location resolveLink(Link link) {
        return resolveLink(link.getURI());
    }

    public native LinkDestination resolveLinkDestination(String uri);

    public LinkDestination resolveLinkDestination(OutlineIterator.OutlineItem item) {
        return resolveLinkDestination(item.uri);
    }

    public LinkDestination resolveLinkDestination(Outline link) {
        return resolveLinkDestination(link.uri);
    }

    public LinkDestination resolveLinkDestination(Link link) {
        return resolveLinkDestination(link.getURI());
    }

    public native Outline[] loadOutline();

    public native OutlineIterator outlineIterator();

    public native String getMetaData(String key);

    public native void setMetaData(String key, String value);

    public native boolean isReflowable();

    public native void layout(float width, float height, float em);

    public native Location findBookmark(long mark);

    public native long makeBookmark(int chapter, int page);

    public long makeBookmark(Location loc) {
        return makeBookmark(loc.chapter, loc.page);
    }

    public static final int PERMISSION_PRINT = (int) 'p';
    public static final int PERMISSION_COPY = (int) 'c';
    public static final int PERMISSION_EDIT = (int) 'e';
    public static final int PERMISSION_ANNOTATE = (int) 'n';

    public native boolean hasPermission(int permission);

    public native boolean isUnencryptedPDF();

    public native String formatLinkURI(LinkDestination dest);

    public boolean isPDF() {
        return false;
    }
}
